/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_VERTEXDECLARATION_H
#define M_VERTEXDECLARATION_H

#include <vector>

namespace Engine
{
	/// Vertex element semantics, used to identify the meaning of vertex buffer contents
	enum VertexElementSemantic
	{
		/// Position, 3 reals per vertex
		VES_POSITION = 1,
		/// Blending weights
		VES_BLEND_WEIGHTS = 2,
		/// Blending indices
		VES_BLEND_INDICES = 3,
		/// Normal, 3 reals per vertex
		VES_NORMAL = 4,
		/// Diffuse colours
		VES_DIFFUSE = 5,
		/// Specular colours
		VES_SPECULAR = 6,
		/// Texture coordinates
		VES_TEXTURE_COORDINATES = 7,
		/// Binormal (Y axis if normal is Z)
		VES_BINORMAL = 8,
		/// Tangent (X axis if normal is Z)
		VES_TANGENT = 9
	};

	/// Vertex element type, used to identify the base types of the vertex contents
	enum VertexElementType
	{
		VET_FLOAT1 = 0,
		VET_FLOAT2 = 1,
		VET_FLOAT3 = 2,
		VET_FLOAT4 = 3,
		/// alias to more specific colour type - use the current rendersystem's colour packing
		VET_COLOR  = 4,
		VET_SHORT1 = 5,
		VET_SHORT2 = 6,
		VET_SHORT3 = 7,
		VET_SHORT4 = 8,
		VET_UBYTE4 = 9,
		/// D3D style compact colour
		VET_COLOR_ARGB = 10,
		/// GL style compact colour
		VET_COLOR_ABGR = 11
	};

	/**
	*  This class declares the usage of a single vertex buffer as a component of a complete VertexDeclaration.
	*/
	class VertexElement
	{
	public:

		friend class VertexDeclaration;

		/**
		*   Gets the vertex buffer index from where this element draws it's values
		*/
		unsigned short GetSource(void) const { return m_source; }

		/**
		*  Gets the offset into the buffer where this element starts
		*/
		size_t GetOffset(void) const { return m_offset; }

		/**
		*   Gets the data format of this element
		*/
		VertexElementType GetType(void) const { return m_type; }

		/**
		*  Gets the meaning of this element
		*/
		VertexElementSemantic GetSemantic(void) const { return m_semantic; }

		/**
		*  Gets the index of this element, only applicable for repeating elements
		*/
		unsigned short GetIndex(void) const { return m_index; }

		/**
		*   Gets the size of this element in bytes
		*/
		size_t GetSize(void) const;

	private:

		/**
		*   Constructor, should not be called directly, call VertexDeclaration::addElement
		*/
		VertexElement(unsigned short source, size_t offset, VertexElementType theType, VertexElementSemantic semantic, unsigned short index = 0);

	private:

		unsigned short m_source; /// The source vertex buffer, as bound to an index using VertexBufferBinding

		size_t m_offset; /// The offset in the buffer that this element starts at

		VertexElementType m_type; /// The type of element

		VertexElementSemantic m_semantic; /// The meaning of the element

		unsigned short m_index; /// Index of the item, only applicable for some elements like texture coords
	};

	/**
	*  This class declares the format of a set of vertex inputs, which can be issued to the rendering API.
	*/
	class VertexDeclaration
	{
	public:
		typedef std::vector<VertexElement> VertexElementList;

		virtual ~VertexDeclaration();

		/**
		*   Get the number of elements in the declaration.
		*/
		size_t GetElementCount(void) const { return m_elementList.size(); }

		/**
		*   Get a single element.
		*/
		const VertexElement* GetElement(unsigned short index) const;

		/**
		*   Adds a new VertexElement to this declaration.
		*/
		virtual const VertexElement& AddElement(unsigned short source, size_t offset, VertexElementType theType, VertexElementSemantic semantic, unsigned short index = 0);

		/**
		*   Remove the element at the given index from this declaration.
		*/
		virtual void RemoveElement(unsigned short elem_index);

		/**
		*   Remove the element at the given index from this declaration.
		*/
		virtual void RemoveElement(const VertexElement* elem);

		/**
		*   Remove all elements.
		*/
		virtual void RemoveAllElements(void);

		/**
		*   Gets the vertex size defined by this declaration for a given source.
		*/
        virtual size_t GetVertexSize(unsigned short source)const;

		/**
		*   Finds a VertexElement with the given semantic, and index if there is more thanone element with the same semantic.
		*/
		virtual const VertexElement* FindElementBySemantic(VertexElementSemantic sem, unsigned short index = 0)const;

		/**
		*   Update offsets for all elements.
		*/
		virtual void UpdateOffsets();

	protected:

		/**
		*  Standard constructor
		*/
		VertexDeclaration();

	protected:
		VertexElementList m_elementList;
	};
}

#endif
